/*-------------------------------------------------------------------------
 *
 * predicate_internals.h
 *	  POSTGRES 内部谓词锁定定义。
 *
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * src/include/storage/predicate_internals.h
 *
 *-------------------------------------------------------------------------
 */
#ifndef PREDICATE_INTERNALS_H
#define PREDICATE_INTERNALS_H

#include "storage/lock.h"
#include "storage/lwlock.h"

/*
 * 提交编号。
 */
typedef uint64 SerCommitSeqNo;

/*
 * 保留的提交序列号：
 *	- 0 被保留以指示不存在的 SLRU 条目；不能用作 SerCommitSeqNo，即使是无效的
 *	- InvalidSerCommitSeqNo 用于指示尚未提交的事务，因此使用一个大于所有有效序列号的数字来进行比较
 *	- RecoverySerCommitSeqNo 用于指代在崩溃/恢复之前发生的事务，因为我们在这一点上重新启动序列。它比所有正常序列号都早，仅被恢复后的准备事务使用
 */
#define InvalidSerCommitSeqNo		((SerCommitSeqNo) PG_UINT64_MAX)
#define RecoverySerCommitSeqNo		((SerCommitSeqNo) 1)
#define FirstNormalSerCommitSeqNo	((SerCommitSeqNo) 2)

/*
 * SERIALIZABLEXACT 结构包含支持 SSI 技术所需的每个可序列化数据库事务的信息。
 *
 * 在共享内存中维护一个自定义列表来管理这些事务。
 * 当可序列化事务获取快照时，将使用一个条目。
 * 除非事务被回滚，否则此条目通常必须保留，直到所有并发事务完成。（对于只读事务，有特殊的优化，通常允许它们更早地清理。）被回滚的事务会尽快被清理。
 *
 * 已提交事务的清理资格通常通过将事务的 finishedBefore 字段与 SxactGlobalXmin 进行比较来确定。
 */
typedef struct SERIALIZABLEXACT
{
	VirtualTransactionId vxid;	/* 正在执行的进程总是有其中一个。 */

	/*
	 * 我们使用两个数字来跟踪事务提交的顺序。在提交之前，事务被标记为已准备，prepareSeqNo 被设置。
	 * 提交后不久，它被标记为已提交，commitSeqNo 被设置。
	 * 这并没有提供严格的提交顺序，但这两个值加在一起对我们来说足够好，因为如果我们不能确定两个提交的确切顺序，就可以安全地假设存在冲突。
	 *
	 * 请注意，在提交处理期间，即使不使用两阶段提交，事务在短时间内也会被标记为已准备。但是使用两阶段提交时，事务可以在准备状态下保持一段时间。
	 */
	SerCommitSeqNo prepareSeqNo;
	SerCommitSeqNo commitSeqNo;

	/* 这些值并不是同时都重要 */
	union
	{
		SerCommitSeqNo earliestOutConflictCommit;	/* 当与
													 * 冲突提交时 */
		SerCommitSeqNo lastCommitBeforeSnapshot;	/* 当未提交或
													 * 没有冲突时 */
	}			SeqNo;
	SHM_QUEUE	outConflicts;	/* 我们无法读取的数据的写事务列表。 */
	SHM_QUEUE	inConflicts;	/* 看不到我们写入的读事务列表。 */
	SHM_QUEUE	predicateLocks; /* 相关的 PREDICATELOCK 对象列表 */
	SHM_QUEUE	finishedLink;	/* 在
								 * FinishedSerializableTransactions 中的列表链接 */

	/*
	 * perXactPredicateListLock 仅在并行查询中使用：它保护该 SERIALIZABLEXACT 的谓词锁列表不受同一会话的其他工作线程的干扰。
	 */
	LWLock		perXactPredicateListLock;

	/*
	 * 对于只读事务：与我们可能存在冲突的并发读写事务的列表，反之亦然对于读写事务
	 */
	SHM_QUEUE	possibleUnsafeConflicts;

	TransactionId topXid;		/* 事务的顶级 xid，如果存在；否则无效 */
	TransactionId finishedBefore;	/* 无效表示仍在运行；否则该结构在没有序列化 xid 早于此时过期。 */
	TransactionId xmin;			/* 事务的快照 xmin */
	uint32		flags;			/* 以下定义值的或组合 */
	int			pid;			/* 相关进程的 pid */
	int			pgprocno;		/* 相关进程的 pgprocno */
} SERIALIZABLEXACT;

#define SXACT_FLAG_COMMITTED			0x00000001	/* 已经提交 */
#define SXACT_FLAG_PREPARED				0x00000002	/* 即将提交 */
#define SXACT_FLAG_ROLLED_BACK			0x00000004	/* 已经回滚 */
#define SXACT_FLAG_DOOMED				0x00000008	/* 将会回滚 */
/*
 * 以下标志实际上意味着被标记的事务与一个
 * 在其之前提交的事务存在冲突。给这个状态起一个合理长度的名称是困难的。
 */
#define SXACT_FLAG_CONFLICT_OUT			0x00000010
#define SXACT_FLAG_READ_ONLY			0x00000020
#define SXACT_FLAG_DEFERRABLE_WAITING	0x00000040
#define SXACT_FLAG_RO_SAFE				0x00000080
#define SXACT_FLAG_RO_UNSAFE			0x00000100
#define SXACT_FLAG_SUMMARY_CONFLICT_IN	0x00000200
#define SXACT_FLAG_SUMMARY_CONFLICT_OUT 0x00000400
/*
 * 以下标志表示事务已经部分释放，但由于并行工作者可能有对它的引用，因此将其保留。事务结束时将由领导者回收。
 */
#define SXACT_FLAG_PARTIALLY_RELEASED	0x00000800

/*
 * 以下类型用于提供持有 SERIALIZABLEXACT 对象的临时列表。使用 HTAB 过于复杂，因为不需要通过键来访问这些对象——在需要的地方有直接指向这些对象的指针。如果创建了一个共享内存列表，这些类型可能会被消除，转而使用通用解决方案。
 */
typedef struct PredXactListElementData
{
	SHM_QUEUE	link;
	SERIALIZABLEXACT sxact;
}			PredXactListElementData;

typedef struct PredXactListElementData *PredXactListElement;

#define PredXactListElementDataSize \
		((Size)MAXALIGN(sizeof(PredXactListElementData)))

typedef struct PredXactListData
{
	SHM_QUEUE	availableList;
	SHM_QUEUE	activeList;

	/*
	 * 这些全局变量在注册和清理可序列化事务时维护。它们必须在所有后端之间是全局的，但在 predicate.c 源文件之外不需要。受 SerializableXactHashLock 保护。
	 */
	TransactionId SxactGlobalXmin;	/* 活动可序列化事务的全局 xmin */
	int			SxactGlobalXminCount;	/* 有多少活动的可序列化事务具有此 xmin */
	int			WritableSxactCount; /* 有多少非只读的可序列化事务处于活动状态 */
	SerCommitSeqNo LastSxactCommitSeqNo;	/* 压制可序列化事务提交的严格单调递增数字 */
	/* 由 SerializableXactHashLock 保护。 */
	SerCommitSeqNo CanPartialClearThrough;	/* 可以通过此序列号清除提交事务的谓词锁和 inConflicts */
	/* 由 SerializableFinishedListLock 保护。 */
	SerCommitSeqNo HavePartialClearedThrough;	/* 已通过此序列号清除 */
	SERIALIZABLEXACT *OldCommittedSxact;	/* 虚拟 sxact 的共享副本 */

	PredXactListElement element;
}			PredXactListData;

typedef struct PredXactListData *PredXactList;

#define PredXactListDataSize \
		((Size)MAXALIGN(sizeof(PredXactListData)))


/*
 * 以下类型用于提供交易对之间 rw 冲突的列表。由于需要完全相同的信息，它们还用于记录可能的不安全事务关系，以识别只读事务的安全快照。
 *
 * 当 RWConflictData 不用于记录交易对之间的任何类型的关系时，它将保留在“可用”列表上。outLink 字段用于维护该列表。
 */
typedef struct RWConflictData
{
	SHM_QUEUE	outLink;		/* 从 sxact 输出的冲突列表链接 */
	SHM_QUEUE	inLink;			/* 指向 sxact 的冲突列表链接 */
	SERIALIZABLEXACT *sxactOut;
	SERIALIZABLEXACT *sxactIn;
}			RWConflictData;

typedef struct RWConflictData *RWConflict;

#define RWConflictDataSize \
		((Size)MAXALIGN(sizeof(RWConflictData)))

typedef struct RWConflictPoolHeaderData
{
	SHM_QUEUE	availableList;
	RWConflict	element;
}			RWConflictPoolHeaderData;

typedef struct RWConflictPoolHeaderData *RWConflictPoolHeader;

#define RWConflictPoolHeaderDataSize \
		((Size)MAXALIGN(sizeof(RWConflictPoolHeaderData)))


/*
 * SERIALIZABLEXIDTAG 结构标识分配给可序列化事务或其任何子事务的 xid。
 */
typedef struct SERIALIZABLEXIDTAG
{
	TransactionId xid;
} SERIALIZABLEXIDTAG;

/*
 * SERIALIZABLEXID 结构提供了从可序列化事务的 TransactionId 到相关 SERIALIZABLEXACT 记录的链接，即使该事务已完成且其连接已关闭。
 *
 * 当新的顶级事务 ID 首次分配给参与谓词锁定的事务时，会创建这些。对于不进行任何写入的特定事务，这可能永远不会发生。它们与相关的可序列化事务对象一起被移除。
 *
 * SubTransGetTopmostTransaction 方法在必要时用于从可能来自子事务的 XID 获取顶级 XID。
 */
typedef struct SERIALIZABLEXID
{
	/* 哈希键 */
	SERIALIZABLEXIDTAG tag;

	/* 数据 */
	SERIALIZABLEXACT *myXact;	/* 指向顶级事务数据的指针 */
} SERIALIZABLEXID;


/*
 * PREDICATELOCKTARGETTAG 结构标识可以作为谓词锁定目标的数据库对象。
 *
 * 请注意，所使用的哈希函数未能正确尊重标记长度——如果结构的长度不是四字节的倍数，它将转到标记末尾后的四字节边界。如果您更改此结构，请确保任何松弛空间已初始化，以便中间或末尾的随机字节不会包含在哈希中。
 *
 * TODO SSI：如果我们始终对同一种值使用相同的字段，则应重命名这些字段。在确保没有例外之前暂停。由于索引是包含块和元组的关系，因此重命名很可能是可行的。如果不行，我们可能需要将最后一个字段分开，并使用它的一部分作为目标类型，以便我们知道如何解释数据。
 */
typedef struct PREDICATELOCKTARGETTAG
{
	uint32		locktag_field1; /* 一个 32 位 ID 字段 */
	uint32		locktag_field2; /* 一个 32 位 ID 字段 */
	uint32		locktag_field3; /* 一个 32 位 ID 字段 */
	uint32		locktag_field4; /* 一个 32 位 ID 字段 */
} PREDICATELOCKTARGETTAG;

/*
 * PREDICATELOCKTARGET 结构表示存在谓词锁的数据库对象。
 *
 * 这些对象的哈希列表保存在共享内存中。当请求在尚未拥有谓词锁的对象上进行谓词锁定时，会添加一个条目。当从其列表中移除最后一个锁时，条目将被移除。
 */
typedef struct PREDICATELOCKTARGET
{
	/* 哈希键 */
	PREDICATELOCKTARGETTAG tag; /* 可锁对象的唯一标识符 */

	/* 数据 */
	SHM_QUEUE	predicateLocks; /* 与谓词锁定目标关联的 PREDICATELOCK 对象列表 */
} PREDICATELOCKTARGET;


/*
 * PREDICATELOCKTAG 结构标识单个谓词锁。
 *
 * 它是谓词锁定目标（即可锁定对象）和已在该目标上获取锁的可序列化事务的组合。
 */
typedef struct PREDICATELOCKTAG
{
	PREDICATELOCKTARGET *myTarget;
	SERIALIZABLEXACT *myXact;
} PREDICATELOCKTAG;

/*
 * PREDICATELOCK 结构表示单个锁。
 *
 * 当读取相关数据库对象时，或者通过提升多个更细粒度的目标而创建条目。当清理可序列化事务时，与该可序列化事务相关的所有条目会被移除。当多个条目合并为单个粗粒度锁条目时，也可以移除条目。
 */
typedef struct PREDICATELOCK
{
	/* 哈希键 */
	PREDICATELOCKTAG tag;		/* 锁的唯一标识符 */

	/* 数据 */
	SHM_QUEUE	targetLink;		/* PREDICATELOCKTARGET 的谓词锁列表中的列表链接 */
	SHM_QUEUE	xactLink;		
/* 列出在SERIALIZABLEXACT的谓词锁列表中的链接 */
	SerCommitSeqNo commitSeqNo; /* 仅用于汇总谓词锁 */
} PREDICATELOCK;


/*
 * LOCALPREDICATELOCK结构表示数据的本地副本，该数据
 * 也存在于PREDICATELOCK表中，组织用于快速访问，无需
 * 获取LWLock。 这纯粹用于优化。
 *
 * 每个可串行化事务都会创建其自己的本地哈希表来保存
 * 这些的集合。 该信息用于确定何时将多个细粒度
 * 锁升级为单个粗粒度锁。
 * 该信息大致与PREDICATELOCK数据并行维护，但由于
 * 此数据未受到锁的保护，并且仅用于优化启发式，
 * 因此在一些保持精确数据代价高昂的边缘案例中，
 * 允许其漂移。
 *
 * 哈希表在可串行化事务获取快照时创建，在事务完成后释放其内存。
 */
typedef struct LOCALPREDICATELOCK
{
	/* 哈希键 */
	PREDICATELOCKTARGETTAG tag; /* 可锁对象的唯一标识符 */

	/* 数据 */
	bool		held;			/* 锁是持有的，还是仅其子锁？	*/
	int			childLocks;		/* 当前持有的子锁数量 */
} LOCALPREDICATELOCK;


/*
 * 可获取的谓词锁类型。
 */
typedef enum PredicateLockTargetType
{
	PREDLOCKTAG_RELATION,
	PREDLOCKTAG_PAGE,
	PREDLOCKTAG_TUPLE
	/* TODO SSI: 可能需要其他类型用于索引锁定 */
} PredicateLockTargetType;


/*
 * 此结构用于快速捕获所有谓词锁的副本。
 * 目前仅由pg_lock_status函数使用，
 * 而pg_lock_status函数又被pg_locks视图使用。
 */
typedef struct PredicateLockData
{
	int			nelements;
	PREDICATELOCKTARGETTAG *locktags;
	SERIALIZABLEXACT *xacts;
} PredicateLockData;


/*
 * 这些宏定义了我们如何将可锁对象的逻辑ID映射到
 * PREDICATELOCKTARGETTAG的物理字段。使用这些来设置值，
 * 而不是直接访问字段。注意目标的多次求值！
 */
#define SET_PREDICATELOCKTARGETTAG_RELATION(locktag,dboid,reloid) \
	((locktag).locktag_field1 = (dboid), \
	 (locktag).locktag_field2 = (reloid), \
	 (locktag).locktag_field3 = InvalidBlockNumber, \
	 (locktag).locktag_field4 = InvalidOffsetNumber)

#define SET_PREDICATELOCKTARGETTAG_PAGE(locktag,dboid,reloid,blocknum) \
	((locktag).locktag_field1 = (dboid), \
	 (locktag).locktag_field2 = (reloid), \
	 (locktag).locktag_field3 = (blocknum), \
	 (locktag).locktag_field4 = InvalidOffsetNumber)

#define SET_PREDICATELOCKTARGETTAG_TUPLE(locktag,dboid,reloid,blocknum,offnum) \
	((locktag).locktag_field1 = (dboid), \
	 (locktag).locktag_field2 = (reloid), \
	 (locktag).locktag_field3 = (blocknum), \
	 (locktag).locktag_field4 = (offnum))

#define GET_PREDICATELOCKTARGETTAG_DB(locktag) \
	((Oid) (locktag).locktag_field1)
#define GET_PREDICATELOCKTARGETTAG_RELATION(locktag) \
	((Oid) (locktag).locktag_field2)
#define GET_PREDICATELOCKTARGETTAG_PAGE(locktag) \
	((BlockNumber) (locktag).locktag_field3)
#define GET_PREDICATELOCKTARGETTAG_OFFSET(locktag) \
	((OffsetNumber) (locktag).locktag_field4)
#define GET_PREDICATELOCKTARGETTAG_TYPE(locktag)							 \
	(((locktag).locktag_field4 != InvalidOffsetNumber) ? PREDLOCKTAG_TUPLE : \
	 (((locktag).locktag_field3 != InvalidBlockNumber) ? PREDLOCKTAG_PAGE :   \
	  PREDLOCKTAG_RELATION))

/*
 * 两阶段提交状态文件记录。共有两种类型：对于每个
 * 事务，我们生成一个每事务记录和可变数量的每谓词锁记录。
 */
typedef enum TwoPhasePredicateRecordType
{
	TWOPHASEPREDICATERECORD_XACT,
	TWOPHASEPREDICATERECORD_LOCK
} TwoPhasePredicateRecordType;

/*
 * 重构SERIALIZABLEXACT的每事务信息。所需的信息不多，因为大多数
 * 对于恢复的准备事务没有意义。
 *
 * 特别是，我们不会记录准备事务的输入和输出冲突列表，
 * 因为相关的SERIALIZABLEXACT在恢复后将不可用。相反，
 * 我们仅通过设置事务的摘要冲突进出标志来记录每种冲突的存在。
 */
typedef struct TwoPhasePredicateXactRecord
{
	TransactionId xmin;
	uint32		flags;
} TwoPhasePredicateXactRecord;

/* 每锁状态 */
typedef struct TwoPhasePredicateLockRecord
{
	PREDICATELOCKTARGETTAG target;
	uint32		filler;			/* 为了避免回填修复中的长度变化 */
} TwoPhasePredicateLockRecord;

typedef struct TwoPhasePredicateRecord
{
	TwoPhasePredicateRecordType type;
	union
	{
		TwoPhasePredicateXactRecord xactRecord;
		TwoPhasePredicateLockRecord lockRecord;
	}			data;
} TwoPhasePredicateRecord;

/*
 * 定义一个宏用于“空”的SERIALIZABLEXACT引用。
 */
#define InvalidSerializableXact ((SERIALIZABLEXACT *) NULL)


/*
 * 需要意识到谓词锁内部的函数的函数定义。
 */
extern PredicateLockData *GetPredicateLockStatusData(void);
extern int	GetSafeSnapshotBlockingPids(int blocked_pid,
										int *output, int output_size);

#endif							/* PREDICATE_INTERNALS_H */
